use std::path::Path;
use std::fs::File;
use std::io::Write;

pub mod traits;
use self::traits::Skynet;

pub mod sphere;
use self::sphere::Sphere;

pub mod points;
pub mod paths;
pub mod vectors;
pub mod boxes;
pub mod mesh;

pub fn distance<T: Skynet<T>>(a: &Vec<T>, b: &Vec<T>) -> T {
    let na = a.len();
    let nb = b.len();
    assert_eq!(na, nb);
    let mut d: T = T::from(0.0);
    for i in 0 .. na {
        let t = a[i] - b[i];
        d += t * t;
    }
    return d.sqrt();
}

pub fn vector_norm<T: Skynet<T>>(x: &Vec<T>) -> T {
    let mut norm = T::from(0.0);
    for j in 0 .. x.len() {
        norm += x[j] * x[j];
    }
    return norm.sqrt();
}

pub fn vector_unit<T: Skynet<T>>(x: &mut Vec<T>) {
    let norm = vector_norm(x);
    for j in 0 .. x.len() {
        x[j] /= norm;
    }
}

pub fn output_view(name: &str) {
    let mut view = String::new();
    view += format!("#declare sky_xyz = \
                     sky_r * vrotate(vrotate(-z, sky_v * x), -sky_u * y);\n").as_str();
    view += "#declare upward = vrotate(vrotate(y, sky_v * x), -sky_u * y);\n\n";
    view += "#declare incline = vaxis_rotate(upward, sky_xyz, theta);\n";
    view += "#declare sun_xyz = vaxis_rotate(sky_xyz, incline, -45);\n";
    view += "#declare sky_xyz = sky_xyz + bs_center;\n";
    view += "#declare sun_xyz = sun_xyz + bs_center;\n\n";
    view += "camera {\n";
    view += "  location sky_xyz\n";
    view += "  sky incline\n";
    view += "  look_at bs_center\n}\n\n";
    view += "light_source {\n";
    view += "  sun_xyz\n";
    view += "  color White\n";
    view += "  shadowless\n}\n";
    
    let path = format!("{}_view.inc", name);
    let mut file = File::create(Path::new(path.as_str())).unwrap();
    file.write_all(view.as_bytes()).unwrap();
}

pub fn output_scene<T: Skynet<T>>(sphere: &Sphere<T>, name: &str) {
    let mut scene = String::new();
    scene += "#version 3.7;\n\
              #include \"colors.inc\"\n\
              global_settings {assumed_gamma 1.0}\n\n";
    
    scene += "// 相机参数（可根据需要，自行修改）\n\
              #declare sky_u = 0;\n\
              #declare sky_v = 0;\n\
              #declare theta = 0;\n\
              #declare odc = 3; // Object distance coefficient.\n\n";
    
    scene += "// 系统参数（不建议自行修改）\n";
    scene += format!("#declare bs_center = <{0:.3}, {1:.3}, {2:.3}>; // 包围球中心\n",
                     sphere.center[0],
                     sphere.center[1],
                     sphere.center[2].inv() /* 变换到左手系 */).as_str();
    scene += format!("#declare bs_r = {:.3}; // 包围球半径\n", sphere.radius).as_str();
    scene += "#declare sky_r = odc * bs_r; // 天球半径\n\n";

    scene += "// 点、矢量、线段的尺寸参数\n";
    scene += "#declare point_size = 0.0025 * sky_r;\n";
    scene += "#declare line_width = 0.75 * point_size;\n";
    scene += "#declare arrow_size = point_size;\n\n";
    
    scene += format!("#include \"{}_view.inc\"\n", name).as_str();
    scene += format!("#include \"{}_object.inc\"\n", name).as_str();
    let path = format!("{}.pov", name);
    let mut file = File::create(Path::new(path.as_str())).unwrap();
    file.write_all(scene.as_bytes()).unwrap();
}

pub fn output_ini_file(name: &str) {
    let mut ini = String::new();
    ini += format!("Input_File_Name=\"{}.pov\"\n", name).as_str();
    ini += "Initial_Frame=1\n";
    ini += "Final_Frame=60\n";
    let ini_file_name = format!("{}.ini", name);
    let mut ini_file = File::create(Path::new(ini_file_name.as_str())).unwrap();
    ini_file.write_all(ini.as_bytes()).unwrap();
}
